/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.volatileengine.renderer.lwjgl;

import javax.vecmath.Tuple2f;
import javax.vecmath.Tuple3f;
import javax.vecmath.Tuple4f;

import org.lwjgl.opengl.ARBShaderObjects;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GLContext;
import org.lwjgl.opengl.Util;

import com.volatileengine.VolatileEngineException;
import com.volatileengine.material.Pass;
import com.volatileengine.material.shader.variable.FloatShaderVariable;
import com.volatileengine.material.shader.variable.IntShaderVariable;
import com.volatileengine.material.shader.variable.ShaderVariable;
import com.volatileengine.material.shader.variable.TextureShaderVariable;
import com.volatileengine.material.shader.variable.Vector2fShaderVariable;
import com.volatileengine.material.shader.variable.Vector3fShaderVariable;
import com.volatileengine.material.shader.variable.Vector4fShaderVariable;
import com.volatileengine.material.states.DepthRenderState;
import com.volatileengine.material.states.RenderState.ApplyFace;
import com.volatileengine.material.states.RenderState.CompareFunc;

/**
 * 
 * @author Administrator
 */
class LWJGLMaterialRenderer {

	private LWJGLTextureRenderStateRenderer texRenderer;

	public LWJGLMaterialRenderer() {
		texRenderer = new LWJGLTextureRenderStateRenderer();
	}

	public void render(Pass pass) {
		if (GLContext.getCapabilities().GL_ARB_shading_language_100 == false) {
			throw new VolatileEngineException("Graphics card does not handle ARB_shading_language_100");
		}

		set(pass.getDepthRenderState());

		ARBShaderObjects.glUseProgramObjectARB(pass.getShaderProgram().getHandle().getIdentity());

		// upload uniforms
		for (int i = 0; i < pass.getShaderProgram().getShaderUniforms().size(); i++) {
			ShaderVariable<?> uniform = pass.getShaderProgram().getShaderUniforms().get(i);
			if (uniform.getHandle().equals(uniform) == false) {
				// upload
				uploadShaderUniform(uniform);
			}
		}

		texRenderer.render(pass.getTextureRenderState());
	}

	private void uploadShaderUniform(ShaderVariable<?> uniform) {
		if (uniform instanceof IntShaderVariable) {
			uploadShaderUniform((IntShaderVariable) uniform);
		} else if (uniform instanceof FloatShaderVariable) {
			uploadShaderUniform((FloatShaderVariable) uniform);
		} else if (uniform instanceof Vector2fShaderVariable) {
			uploadShaderUniform((Vector2fShaderVariable) uniform);
		} else if (uniform instanceof Vector3fShaderVariable) {
			uploadShaderUniform((Vector3fShaderVariable) uniform);
		} else if (uniform instanceof Vector4fShaderVariable) {
			uploadShaderUniform((Vector4fShaderVariable) uniform);
		} else if (uniform instanceof TextureShaderVariable) {
			uploadShaderUniform((TextureShaderVariable) uniform);
		}
	}

	private void uploadShaderUniform(IntShaderVariable uni) {
		int v = uni.getValue();
		ARBShaderObjects.glUniform1iARB(uni.getHandle().getIdentity(), v);
	}

	private void uploadShaderUniform(FloatShaderVariable uni) {
		float v = uni.getValue();
		ARBShaderObjects.glUniform1fARB(uni.getHandle().getIdentity(), v);
	}

	private void uploadShaderUniform(Vector2fShaderVariable uni) {
		Tuple2f v = uni.getValue();
		ARBShaderObjects.glUniform2fARB(uni.getHandle().getIdentity(), v.x, v.y);
	}

	private void uploadShaderUniform(Vector3fShaderVariable uni) {
		Tuple3f v = uni.getValue();
		ARBShaderObjects.glUniform3fARB(uni.getHandle().getIdentity(), v.x, v.y, v.z);
	}

	private void uploadShaderUniform(Vector4fShaderVariable uni) {
		Tuple4f v = uni.getValue();
		ARBShaderObjects.glUniform4fARB(uni.getHandle().getIdentity(), v.x, v.y, v.z, v.w);
	}

	private void uploadShaderUniform(TextureShaderVariable uni) {
		int v = uni.getValue().getHandle().getIdentity();
		ARBShaderObjects.glUniform1iARB(uni.getHandle().getIdentity(), v);
	}

	private void set(DepthRenderState object) {
		if (object == null) {
			return;
		}
		if (object.isEnabled()) {
			GL11.glEnable(GL11.GL_DEPTH_TEST);
			GL11.glDepthFunc(convertCompareFunc(object.getDepthFunc()));
			Util.checkGLError();
		} else {
			GL11.glDisable(GL11.GL_DEPTH_TEST);
		}
	}

	protected int convertCompareFunc(CompareFunc p) {
		switch (p) {
		case ALWAYS:
			return GL11.GL_ALWAYS;
		case EQUAL:
			return GL11.GL_EQUAL;
		case GREATER_OR_EQUAL:
			return GL11.GL_GEQUAL;
		case GREATER:
			return GL11.GL_GREATER;
		case LESS_OR_EQUAL:
			return GL11.GL_LEQUAL;
		case LESS:
			return GL11.GL_LESS;
		case NEVER:
			return GL11.GL_NEVER;
		case NOT_EQUAL:
			return GL11.GL_NOTEQUAL;
		default:
			// to satisfy the compiler
			return -1;
		}
	}

	protected int convertApplyFace(ApplyFace face) {
		switch (face) {
		case BACK:
			return GL11.GL_BACK;
		case FRONT:
			return GL11.GL_FRONT;
		case FRONT_AND_BACK:
			return GL11.GL_FRONT_AND_BACK;
		default:
			// to satisfy the compiler
			return -1;
		}
	}
}
